/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *    2011 year
 *    Author: Michael Khromov aka MikeDM (Mike D'Morto)
 *    E-Mail: mikedmorto@gmail.com
 */

#include "mcore.h"


MCore::MCore(QObject *parent) :
    QObject(parent){
    this->lng=DEFLNG;
    this->logger = new MLog();

}

void MCore::start(){
    QApplication::setWindowIcon(QIcon(QString(IMG_PREFIX)+"/core/icon.svg"));
    // флаги для показа помощи и версии
    bool fH = false;
    bool fV = false;

    // русские буковки в консоли. для никсов с utf8
    QTextCodec::setCodecForTr(QTextCodec::codecForName("utf8"));
    //QTextCodec::setCodecForCStrings( QTextCodec::codecForName("utf8") );
    QTextCodec::setCodecForCStrings(QTextCodec::codecForLocale());

        // парсим параметры запуска
    for(int i=0 ; i<QApplication::arguments().count() ; i++ ){
                if(QApplication::arguments().at(i)=="-l" or QApplication::arguments().at(i)=="--log"){
                    // указан лог. проверяем следующий элемент, если есть то это и есть путь до лога.
                    // и мы его включаем логгеру
                    if(QApplication::arguments().size()>i){
                        this->logger->setFile(QApplication::arguments().at(i+1));
                    }
                }

                // внезапно юзверю стало интерестно какая у нас версия, получите распишитесь =)
                if(QApplication::arguments().at(i)=="-v" or QApplication::arguments().at(i)=="--version"){
                    fV=true;
                }
                // проверка на установку уровня отладки
                if(QApplication::arguments().at(i)=="-d" or QApplication::arguments().at(i)=="--debug"){
                    if(QApplication::arguments().size()>i){
                        this->logger->setLevel(QApplication::arguments().at(i+1).toInt());
                    }
                }
                // запрос помощи по ключам
                if(QApplication::arguments().at(i)=="-h" or QApplication::arguments().at(i)=="--help"){

                    fH=true;
                }

                if(QApplication::arguments().at(i)=="-db" or QApplication::arguments().at(i)=="--database"){
                    if(QApplication::arguments().size()>i){
                        // здесь мы получили волшебную строку настройки подключения к базе дынных
                        this->db=QApplication::arguments().at(i+1).split(",");
                    }
                }
                if(QApplication::arguments().at(i)=="-lg" or QApplication::arguments().at(i)=="--logon-plugin"){
                    if(QApplication::arguments().size()>i){
                        /*а здесь мы получаем волшебную строку настройки плагина для входа в систему
                          на вопрос "а зачем нужно это????" ответ:
                          для гибкости, ибо не только по логину паролю бывает авторизация пользвателя =)
                          внезапно да? так вот, для того что бы гибко обеспечить администраторов этой системы
                          можно налету менять систему авторизации пользователя,
                          одна конфигурация входит по логину паролю с плагином std_logon
                          а другая с плагином foot_logon по отпечатку правой ноги.
                          ну и в том же духе хоть по запоху из рта, на усмотрение фантазии разработчика.
                          Это еще позволит использовать внешнии системы подтверждения личности пользователя
                          например двойные ключи итд итп. вобщем нужная штука.
                        */
                        this->lg=QApplication::arguments().at(i+1).split(",");
                    }
                }

                if(QApplication::arguments().at(i)=="-lng" or QApplication::arguments().at(i)=="--language"){
                    if(QApplication::arguments().size()>i){
                        this->lng=QApplication::arguments().at(i+1);
                    }
                }
    }




    // подключаем перевод.
    QTranslator * trans_core = new QTranslator();
                if(trans_core->load(QString("%1/core/%2.qm").arg(LANG_PREFIX).arg(this->lng))){
                    QCoreApplication::installTranslator(trans_core);
                    logger->slotPut(CORE,3,QString(tr("Debug: success translate %1")).arg(this->lng));
                }else{
                    logger->slotPut(CORE,1,QString("fail translate %1").arg(this->lng));
                }

                if(fV){
                    QString s =tr("%1 version %2.%3 build %4 reversion %5").arg(APP_NAME).arg(
                            QString(VERSION)).arg(
                            QString(SUBVERSION)).arg(
                            QString(BUILD)).arg(
                            QString(REVERSION));
                    QTextStream out(stdout);
                    out << s << endl;
                    exit(0);
                }
                if(fH){
                    QTextStream out(stdout);
                    out << tr("Usage:") << endl;
                    out << tr("-h or --help\t\tfor print this help") << endl;
                    out << tr("-v or --version\t\tfor show version") << endl;
                    out << tr("-d or --debug\t\tfor set debug level 0 to 3") << endl;
                    out << tr("-db or --database\tfor set up database") << endl;
                    out << tr("-l or --log\t\tfor setup log file") << endl;
                    out << tr("-lg or --logon-plugin\tfor set up logon system")<<endl;
                    exit(0);

                }


    // сигналим о начале работы
    this->logger->slotPut("сore",2,tr("Start"));
    // проверяем наличие конфигурации базы данных
    if(this->db.size()==0){
            // настроек нету. ругаемся и тушим приложение
            this->logger->slotPut(CORE,1,tr("Error: database not set."));
            QMessageBox::critical(NULL,APP_NAME,tr("Error: database not set."));
            exit(1);
    }

    // подгружаем библиотеку для работы с бд.
    // указатель на объект базы данных
    this->logger->slotPut(CORE,2,tr("Load database library \"%1\"").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));
    iDatabase * iDb;
    // проверяем наличие файла библиотеки

    if(!QFile::exists(QString(QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)))){
        // файлика нету. беда беда. ругаемся и выключаемся.
        this->logger->slotPut(CORE,1,tr("Error: database library %1 not found.").arg(
                                 QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: database library %1 not found.").arg(
                                  QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));
        exit(2);
    }
    // файлик есть, пробуем подгрузить либу.
    QPluginLoader loader;
    loader.setFileName(QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX));
    if(!loader.load()){
        // подгрузка библиотеки не увенчалась успехом, либу подменили либо она повреждена либо еще какая нибудь фигня.
        QString errstr=loader.errorString();
        this->logger->slotPut(CORE,1,tr("Error: failure load database library \"%1\" with error string \"%2\".").arg(
                                 QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)).arg(
                                 errstr));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: failure load database library \"%1\" with error string \"%2\".").arg(
                                  QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)).arg(
                                  errstr));
        exit(3);
    }
    this->logger->slotPut(CORE,2,tr("Database library \"%1\" is loaded").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));
    // получаем инстанс библиотеки и проводим инициализацию плагина предварительно связав его с объектом протоколов
    this->logger->slotPut(CORE,2,tr("Init database library \"%1\" ").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));
    iDb = qobject_cast<iDatabase*>(loader.instance());
    this->connect(iDb,SIGNAL(sigPutLog(QString,int,QString)),this->logger,SLOT(slotPut(QString,int,QString))
                  ,Qt::DirectConnection);
    if(!iDb->init(this->db, this->lng)){
        // в процессе инициализации произошли неполадки, ругаемся и показываем ошибку и закрываем приложение
        this->logger->slotPut(CORE,1,tr("Error: failure init database library. Last error \"%1\".").arg(iDb->getlastError()));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: failure init database library. Last error \"%1\".").arg(
                                  iDb->getlastError()));
        exit(4);
    }
    this->logger->slotPut(CORE,2,tr("Init database library \"%1\" is success").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"db_"+this->db.at(0)+QString(PLUGIN_SUFFIX)));

    // *********************************************************************************************
    // плагин баз данных инициирован удачно, теперь подгружаем плагин для логина пользователя

    // проверяем наличие конфигурации плагина авторизации
    if(this->lg.size()==0){
            // настроек нету. ругаемся и тушим приложение
            this->logger->slotPut(CORE,1,tr("Error: logon plugin not set."));
            QMessageBox::critical(NULL,APP_NAME,tr("Error: logon plugin not set."));
            exit(5);
    }
    this->logger->slotPut(CORE,2,tr("Load logon library \"%1\"").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));
    iLogon * iLg;
    // проверяем наличие файла библиотеки

    if(!QFile::exists(QString(QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)))){
        // файлика нету. беда беда. ругаемся и выключаемся.
        this->logger->slotPut(CORE,1,tr("Error: logon library %1 not found.").arg(
                                 QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: logon library %1 not found.").arg(
                                  QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));
        exit(6);
    }
    // файлик есть, пробуем подгрузить либу.
    loader.setFileName(QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX));
    if(!loader.load()){
        // подгрузка библиотеки не увенчалась успехом, либу подменили либо она повреждена либо еще какая нибудь фигня.
        QString errstr=loader.errorString();
        this->logger->slotPut(CORE,1,tr("Error: failure load logon library \"%1\" with error string \"%2\".").arg(
                                 QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)).arg(
                                 errstr));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: failure load logon library \"%1\" with error string \"%2\".").arg(
                                  QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)).arg(
                                  errstr));
        exit(7);
    }

    this->logger->slotPut(CORE,2,tr("Info: logon library \"%1\" is loaded").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));
    // получаем инстанс logon и проводим инициализацию плагина предварительно связав его с объектом протоколов
    this->logger->slotPut(CORE,2,tr("Info: init logon library \"%1\" ").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));
    iLg = qobject_cast<iLogon*>(loader.instance());

    // привязываем необходимые сигналы

    this->connect(iLg,SIGNAL(sigPutLog(QString,int,QString)),this->logger,SLOT(slotPut(QString,int,QString))
                  ,Qt::DirectConnection);
    this->connect(iLg,SIGNAL(sigQuery(QString,QString,QStringList&)),iDb,SLOT(slotQuery(QString,QString,QStringList&))
                  ,Qt::DirectConnection);
    this->connect(iDb,SIGNAL(sigError(QString,QString)),iLg,SLOT(slotDbError(QString,QString))
                  ,Qt::DirectConnection);
    this->connect(iDb,SIGNAL(sigFinish(QString,int,Mdata*)),iLg,SLOT(slotDbResult(QString,int,Mdata*))
                  ,Qt::DirectConnection);
    this->connect(iLg,SIGNAL(sigFreeRes(int)),iDb,SLOT(slotFreeRes(int))
                  ,Qt::DirectConnection);

    if(!iLg->init(this->lg, this->lng)){
        // в процессе инициализации произошли неполадки, ругаемся и показываем ошибку и закрываем приложение
        this->logger->slotPut(CORE,1,tr("Error: failure init logon library. Last error \"%1\".").arg(iLg->getlastError()));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: failure init logon library. Last error \"%1\".").arg(
                                  iLg->getlastError()));
        exit(8);
    }
    this->logger->slotPut(CORE,2,tr("Info: init logon library \"%1\" is success").arg(
                             QString(PLUGIN_DIR)+QString(PLUGIN_PREFIX)+"logon_"+this->lg.at(0)+QString(PLUGIN_SUFFIX)));


    // иницализации плагина авторизации прошла успешно
    // стартуем авторизацию
    if(!iLg->exec()){
        // все прошло печально. видимо пользователь не смог авторизаваться
        this->logger->slotPut(CORE,2,tr("Info: logon procedure is fail. quit."));
        exit(9);
    }

    // продолжаем захватывать мир, дали добро и пользователь подтвержден. докладываем в лог
    this->username = iLg->getUserName();
    this->logger->slotPut(CORE,2,tr("user \"%1\" is entered").arg(this->username));

    // инициализируем модули
    // соединяем ядро с базой
    connect(this,SIGNAL(sigQuery(QString,QString,QStringList&)),iDb,SLOT(slotQuery(QString,QString,QStringList&)),
            Qt::DirectConnection);
    connect(iDb,SIGNAL(sigError(QString,QString)),this,SLOT(slotDbError(QString,QString)),
            Qt::DirectConnection);
    connect(iDb,SIGNAL(sigFinish(QString,int,Mdata*)),this,SLOT(slotDbResult(QString,int,Mdata*)),
            Qt::DirectConnection);
    connect(this,SIGNAL(sigFreeRes(int)),iDb,SLOT(slotFreeRes(int)),
            Qt::DirectConnection);


    if(!this->getPluginsList()){
        // fek! опять неладно в датском королевстве.
        // ругаемся и закрываемся
        this->logger->slotPut(CORE,1,tr("Error: failure getting list of plugins. Last error \"%1\".").arg(this->lastError));
        QMessageBox::critical(NULL,APP_NAME,tr("Error: failure getting list of plugins. Last error \"%1\".").arg(
                                  this->lastError));
        exit(10);
    }
    // понеслась инициализация плагинов для юзверя
    iWorker * wrk;
    for(int i = 0 ; i < this->pluginsList.size() ; i++){
        this->logger->slotPut(CORE, 3, tr("Debug: start init plugin %1").arg(
                                 this->pluginsList.at(i).pl_name));
        if(!QFile::exists(QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX)))){
            // файлика нету. беда беда. ругаемся и выключаемся.
            this->logger->slotPut(CORE,1,tr("Error: library \"%1\" not found.").arg(
                                     QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX)));
            QMessageBox::critical(NULL,APP_NAME,tr("Error: library \"%1\" not found.").arg(
                                      QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX)));
            exit(11);

        }
        loader.setFileName(QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX)));
        if(!loader.load()){
            // подгрузка библиотеки не увенчалась успехом, либу подменили либо она повреждена либо еще какая нибудь фигня.
            QString errstr=loader.errorString();
            this->logger->slotPut(CORE,1,tr("Error: failure load library \"%1\" with error string \"%2\".").arg(
                                     QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))).arg(
                                     errstr));
            QMessageBox::critical(NULL,APP_NAME,tr("Error: failure load library \"%1\" with error string \"%2\".").arg(
                                      QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))).arg(
                                      errstr));
            exit(12);
        }

        // библиотека в памяти, инициализируем ее
        this->logger->slotPut(CORE,2,tr("Info: library \"%1\" is loaded").arg(
                                 QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))));
        // получаем инстанс logon и проводим инициализацию плагина предварительно связав его с объектом протоколов
        this->logger->slotPut(CORE,2,tr("Info: init library \"%1\" ").arg(
                                 QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))));
        wrk = qobject_cast<iWorker*>(loader.instance());

        // привязываем необходимые сигналы
        connect(wrk,SIGNAL(sigPutLog(QString,int,QString)),this->logger,SLOT(slotPut(QString,int,QString)));

        if(!wrk->init(QStringList(this->pluginsList.at(i).config.split(",")),this->lng)){
            // в процессе инициализации произошли неполадки, ругаемся и показываем ошибку и закрываем приложение
            this->logger->slotPut(CORE,1,tr("Error: failure init library \"%1\". Last error \"%2\".").arg(
                                      QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))).arg(
                                      wrk->getlastError()));
            QMessageBox::critical(NULL,APP_NAME,tr("Error: failure init library \"%1\". Last error \"%2\".").arg(
                                      QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))).arg(
                                      wrk->getlastError()));
            exit(13);
        }
        this->logger->slotPut(CORE,2,tr("Info: init logon library \"%1\" is success").arg(
                                 QString(QString(PLUGIN_DIR)+"/"+this->pluginsList.at(i).pl_name+QString(PLUGIN_SUFFIX))));

        this->workerList.append(wrk);
    }

    // плагины проинициализированны, показываем окошечко с ними.
    this->cdia = new CoreDialog(&this->workerList);
    this->cdia->setWindowIcon(QIcon(QString(IMG_PREFIX)+"/core/icon.svg"));
    cdia->exec();

    //usleep(1000);
    //iDb->slotStop();
    exit(0);
}



//////////////////////////////////////////////////////////////////////////////////////////

bool MCore::getPluginsList(){
    // делаем запрос в базу и ждем когда появится флаг финиша в бесконечном цикле.
    // в это время вся работа кипит в слотах
    QStringList aa;
    aa.append(this->username);
    emit this->sigQuery(CORE,"getPluginsList",aa);
    this->flagPLBone=false;
    this->flagPLBoneK=false;
    while(!this->flagPLBone){
        usleep(100);
    }
    return this->flagPLBoneK;
}

void MCore::slotDbError(const QString &pl_name, const QString &errtext){
    // прилетела ошибка из базы
    if(pl_name==CORE){
        this->lastError=errtext;
        this->flagPLBone=true;
        this->flagPLBoneK=false;
    }

}

void MCore::slotDbResult(const QString &pl_name, int id, Mdata *data){
    //  прилетел асинхронный ответ от базы
    if(pl_name == CORE){
        // прилетело нам
        // вынимаем данные
        while(data->next()){
            // вынимаем данные, убеждаемся что их столько сколько нужно. если это не так то сигналим
            // ошибку
            //qDebug()<<"data size = "<< data->getData().size();
            QStringList aa = data->getData();
            PluginsListItem item;
            item.pl_name=aa.at(0);
            item.config=aa.at(1);
            this->pluginsList.append(item);
        }
        // подтираем за собой
        emit this->sigFreeRes(id);
        this->flagPLBone=true;
        this->flagPLBoneK=true;
    }
}
